package com.bestcem.xm.component.security.retry;

import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.component.security.auth.XmAuthClient;
import com.bestcem.xm.component.security.dto.AuthenticationDTO;
import io.grpc.StatusRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
@ConditionalOnClass(com.bestcem.xm.component.security.auth.XmAuthClient.class)
public class CheckAuthenticationRetry {

    private XmAuthClient xmAuthClient;

    @Autowired
    public void setXmAuthClient(XmAuthClient xmAuthClient) {
        this.xmAuthClient = xmAuthClient;
    }

    public ServiceResult<AuthenticationDTO> callApi(String token, Boolean authentication, String uri,
                                                    String method) {
        // 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试
        Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();
        exceptionMap.put(StatusRuntimeException.class, true);
        // 构建重试模板实例
        RetryTemplate retryTemplate = new RetryTemplate();

        // 设置重试回退操作策略，主要设置重试间隔时间
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(1000L);

        //设置重试次数和异常类型
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(3, exceptionMap);

        retryTemplate.setRetryPolicy(retryPolicy);
        retryTemplate.setBackOffPolicy(backOffPolicy);

        return retryTemplate.execute(
                retryContext -> xmAuthClient.checkAuthentication(token, authentication, uri, method),
                recoveryContext -> {
                    Throwable throwable = recoveryContext.getLastThrowable();
                    log.error("鉴权接口调用失败, token: {}, authentication: {}, uri: {}, method: {}, retryCount: {}",
                            token, authentication, uri, method, recoveryContext.getRetryCount(), throwable);
                    return ServiceResult.fail("鉴权失败");
                });
    }
}
